Course Setup
install.packages("tidyverse")
library(tidyverse)
[30m-- [1mAttaching packages[22m --------------------------------------- tidyverse 1.2.1 --[39m
[30m[32mv[30m [34mggplot2[30m 2.2.1 [32mv[30m [34mpurrr [30m 0.2.4
[32mv[30m [34mtibble [30m 1.3.4 [32mv[30m [34mdplyr [30m 0.7.4
[32mv[30m [34mtidyr [30m 0.7.2 [32mv[30m [34mstringr[30m 1.2.0
[32mv[30m [34mreadr [30m 1.1.1 [32mv[30m [34mforcats[30m 0.2.0[39m
[30m-- [1mConflicts[22m ------------------------------------------ tidyverse_conflicts() --
[31mx[30m [34mdplyr[30m::[32mfilter()[30m masks [34mstats[30m::filter()
[31mx[30m [34mdplyr[30m::[32mlag()[30m masks [34mstats[30m::lag()[39m
First ggplot
ggplot(data = midwest) +
geom_point(mapping = aes(x = popdensity, y = percollege))

Equivalent Code
ggplot(midwest) +
geom_point(aes(x = popdensity, y = percollege))

Your Turn
- Try plotting
popdensity by state.
- Try plotting
county by state.
- Bonus: Try just using the
ggplot(data = midwest) from above.
- What do you get?
- Does this make sense?
Add Aesthetics
ggplot(midwest) +
geom_point(aes(x = popdensity, y = percollege, color = state))

Global Aesthetics
ggplot(midwest) +
geom_point(aes(x = popdensity, y = percollege), color = 'pink')

Your Turn
- Instead of using colors, make the shape of the points different for each state.
- Instead of color, use
alpha instead.
- What does this do to the plot?
- Try the following command:
colors().
- Try a few colors to find your favorite.
- What happens if you use the following code:
ggplot(midwest) +
geom_point(aes(x = popdensity, y = percollege, color = 'green'))
Additional Geoms
ggplot(midwest) +
geom_smooth(aes(x = popdensity, y = percollege))

Add more Aesthetics
ggplot(midwest) +
geom_smooth(aes(x = popdensity, y = percollege, linetype = state),
se = FALSE)

Your Turn
- It is possible to combine geoms, which we will do next, but try it first. Try to recreate this plot.

Layered ggplot
ggplot(midwest) +
geom_point(aes(x = popdensity, y = percollege, color = state)) +
geom_smooth(aes(x = popdensity, y = percollege, color = state),
se = FALSE)

Remove duplicate aesthetics
ggplot(midwest,
aes(x = popdensity, y = percollege, color = state)) +
geom_point() +
geom_smooth(se = FALSE)

Your Turn
- Can you recreate the following figure?

Brief plot customization
ggplot(midwest,
aes(x = popdensity, y = percollege, color = state)) +
geom_point() +
scale_x_continuous("Population Density",
breaks = seq(0, 80000, 20000)) +
scale_y_continuous("Percent College Graduates") +
scale_color_discrete("State")
Brief plot customization Output

Additional ggplot2 resources
R works as a calculator
1 + 2 - 3
[1] 0
5 * 7
[1] 35
2/1
[1] 2
R Calculator 2
sqrt(4)
[1] 2
2^2
[1] 4
Can save objects to use later
x <- 1 + 3
x
[1] 4
x * 3
[1] 12
R is case sensitive
case_sensitive <- 10
Case_sensitive
Error: object 'Case_sensitive' not found
R Functions
set.seed(1)
rnorm(n = 5, mean = 0, sd = 1)
[1] -0.6264538 0.1836433 -0.8356286 1.5952808 0.3295078
set.seed(1)
rnorm(5, 0, 1)
[1] -0.6264538 0.1836433 -0.8356286 1.5952808 0.3295078
set.seed(1)
rnorm(sd = 1, n = 5, mean = 0)
[1] -0.6264538 0.1836433 -0.8356286 1.5952808 0.3295078
Working through Errors
- Use
?function_name to explore the details of the function. The examples at the bottom of every R help page can be especially helpful.
- If this does not help, copy and paste the error and search on the internet.
Using dplyr for data manipulation
The dplyr package uses verbs for common data manipulation tasks. These include:
filter()
count()
arrange()
select()
mutate()
summarise()
Using filter
filter(congress_age, congress == 80)
Save filtered results to object
congress_80 <- filter(congress_age, congress == 80)
Other operators for numbers
Your Turn
- Select all rows where the congress member was older than 80 at the start of the term.
- Use the
is.na function to identify congress members that have missing middlenames.
Filter character variables
filter(congress_age, chamber == 'senate')
Combine Operations - AND
filter(congress_age, congress == 80, chamber == 'senate')
Equivalent AND Statement
filter(congress_age, congress == 80 & chamber == 'senate')
Filter - OR
filter(congress_age, congress == 80 | congress == 81)
%in%
filter(congress_age, congress %in% c(80, 81))
Not Operator
filter(congress_age, congress != 80)
Not Operator 2
filter(congress_age, congress == 80 & !chamber == 'senate')
Working with logicals
filter(congress_age, incumbent)
Your Turn
- Select the Senators from Iowa.
- Select the Senators from Iowa that are not inbumbents.
Using count
count(congress_age, party)
count(congress_age, incumbent)
Using arrange
arrange(congress_age, state)
Add more variables
arrange(congress_age, state, party)
Descending Order
arrange(congress_age, desc(congress))
Your Turn
- Count the number of congress members from each party that are older than 80 at term start.
- Arrange the result from above by the variable n.
Using select
select(congress_age, congress, chamber, party, age)
Helper functions
starts_with()
ends_with()
contains()
matches()
num_range()
:
everything()
starts_with helper
select(congress_age, starts_with('s'))
Contains helper
select(congress_age, contains('name'))
Colon
select(congress_age, congress:birthday)
Drop variables
select(congress_age, -firstname, -state, -party, -incumbent, -chamber)
Reorder with everything
select(congress_age, congress, chamber, incumbent, age, everything())
rename function
rename(congress_age, first_name = firstname, last_name = lastname)
Your Turn
- Using the
dplyr helper functions, select all the variables that start with the letter ‘c’.
- Rename the first three variables in the congress data to ‘x1’, ‘x2’, ‘x3’.
- After renaming the first three variables, use this new data (ensure you saved the previous step to an object) to select these three variables with the
num_range function.
Using mutate
congress_red <- select(congress_age, congress, chamber, state, party)
mutate(congress_red,
democrat = ifelse(party == 'D', 1, 0),
num_democrat = sum(democrat)
)
Your Turn
- Using the
diamonds data, use ?diamonds for more information on the data, use the mutate function to calculate the price per carat. Hint, this operation would involve standardizing the price variable so that all are comparable at 1 carat.
- Using
mutate, calculate the rank of the original price variable and the new price variable calculated above using the min_rank function. Are there differences in the ranking of the prices?
Using summarise
congress_2 <- mutate(congress_age,
democrat = ifelse(party == 'D', 1, 0)
)
summarise(congress_2,
num_democrat = sum(democrat)
)
group_by
congress_grp <- group_by(congress_2, congress)
summarise(congress_grp,
num_democrat = sum(democrat),
total = n(),
prop_democrat = num_democrat / total
)
Explore trend
num_dem <- summarise(congress_grp,
num_democrat = sum(democrat),
total = n(),
prop_democrat = num_democrat / total
)
ggplot(num_dem, aes(x = congress, y = prop_democrat)) +
geom_line()
Explore trend output

Your Turn
- Suppose we wanted to calculate the number and proportion of republicans instead of democrats, assuming these are the only two parties, edit the
summarise command above to calculate these values.
- Suppose instead of using
sum(democrat) above, we used mean(democrat), what does this value return? Why does it return this value?
group_by with mutate
congress_red <- select(congress_age, congress, chamber, state, party)
congress_grp <- group_by(congress_red, congress)
mutate(congress_grp,
democrat = ifelse(party == 'D', 1, 0),
num_democrat = sum(democrat),
total = n(),
prop_democrat = num_democrat / total
)
group_by with mutate output
Chaining operations
summarise(
group_by(
mutate(
filter(
congress_age, congress >= 100
),
democrat = ifelse(party == 'D', 1, 0)
),
congress, chamber
),
num_democrat = sum(democrat),
total = n(),
prop_democrat = num_democrat / total
)
The pipe %>% is the answer
congress_age %>%
filter(congress >= 100) %>%
mutate(democrat = ifelse(party == 'D', 1, 0)) %>%
group_by(congress, chamber) %>%
summarise(
num_democrat = sum(democrat),
total = n(),
prop_democrat = num_democrat / total
)
The two are identical
pipe_congress <- congress_age %>%
filter(congress >= 100) %>%
mutate(democrat = ifelse(party == 'D', 1, 0)) %>%
group_by(congress, chamber) %>%
summarise(
num_democrat = sum(democrat),
total = n(),
prop_democrat = num_democrat / total
)
nested_congress <- summarise(
group_by(
mutate(
filter(
congress_age, congress >= 100
),
democrat = ifelse(party == 'D', 1, 0)
),
congress, chamber
),
num_democrat = sum(democrat),
total = n(),
prop_democrat = num_democrat / total
)
identical(pipe_congress, nested_congress)
[1] TRUE
Your Turn
- Look at the following nested code and determine what is being done. Then translate this code to use the pipe operator.
summarise(
group_by(
mutate(
filter(
diamonds,
color %in% c('D', 'E', 'F') & cut %in% c('Fair', 'Good', 'Very Good')
),
f_color = ifelse(color == 'F', 1, 0),
vg_cut = ifelse(cut == 'Very Good', 1, 0)
),
clarity
),
avg = mean(carat),
sd = sd(carat),
avg_p = mean(price),
num = n(),
summary_f_color = mean(f_color),
summary_vg_cut = mean(vg_cut)
)
Other Useful dplyr functions
There are a set of functions that can greatly simplify data operations. These functions end with:
Simple example
rename_if(congress_age, is.character, toupper)
More realistic example
starwars %>%
group_by(species, gender) %>%
summarise_if(is.numeric, mean, na.rm = TRUE)
Can call more than one function
starwars %>%
group_by(species, gender) %>%
summarise_if(is.numeric, funs(mean, median), na.rm = TRUE) %>%
add_tally()
Data Import
ufo <- read_csv("https://raw.githubusercontent.com/lebebr01/iowa_data_science/master/data/ufo.csv")
Parsed with column specification:
cols(
`Date / Time` = col_character(),
City = col_character(),
State = col_character(),
Shape = col_character(),
Duration = col_character(),
Summary = col_character(),
Posted = col_character()
)
Manual column names
ufo_man <- read_csv("https://raw.githubusercontent.com/lebebr01/iowa_data_science/master/data/ufo.csv",
skip = 1,
col_names = c('Date/Time', 'City', 'State',
'Shape', 'Duration', 'Summary',
'Posted'))
Parsed with column specification:
cols(
`Date/Time` = col_character(),
City = col_character(),
State = col_character(),
Shape = col_character(),
Duration = col_character(),
Summary = col_character(),
Posted = col_character()
)
Manual column names output
ufo_man
Manual column types
ufo_date <- read_csv("Data/ufo.csv",
col_types = list(
'Date / Time' = col_datetime(format = "%m/%d/%y %H:%M"),
City = col_character(),
State = col_character(),
Shape = col_character(),
Duration = col_character(),
Summary = col_character(),
Posted = col_character()
))
number of columns of result is not a multiple of vector length (arg 1)56 parsing failures.
row # A tibble: 5 x 5 col row col expected actual file expected <int> <chr> <chr> <chr> <chr> actual 1 119 Date / Time date like %m/%d/%y %H:%M 12/1/14 'Data/ufo.csv' file 2 194 Date / Time date like %m/%d/%y %H:%M 11/27/14 'Data/ufo.csv' row 3 236 Date / Time date like %m/%d/%y %H:%M 11/24/14 'Data/ufo.csv' col 4 407 Date / Time date like %m/%d/%y %H:%M 11/15/14 'Data/ufo.csv' expected 5 665 Date / Time date like %m/%d/%y %H:%M 10/31/14 'Data/ufo.csv'
... ................. ... .................................................................... ........ .................................................................... ...... .................................................................... .... .................................................................... ... .................................................................... ... .................................................................... ........ ....................................................................
See problems(...) for more details.
Manual column types output
ufo_date
Still problems
problems(ufo_date)
Other text formats
- tsv - tab separated files -
read_tsv
- fixed width files -
read_fwf
- white space generally -
read_table
- delimiter generally -
read_delim
Your Turn
- There is a tsv file posted on icon called “lotr_clean.tsv”. Download this and read this data file into R.
- Instead of specifying the path, use the function
file.choose(). For example, read_tsv(file.choose()).
- What does this function do?
- Would you recommend this to be used in a reproducible document?
Excel Files
install.packages('readxl')
read_excel
library(readxl)
read_excel('data/titanic.xlsx')
Write Output Files
ufo_count <- ufo %>%
group_by(State) %>%
mutate(num_state = n())
write_csv(ufo_count, path = 'path/to/save/file.csv')
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byB0aWR5dmVyc2UvUiINCmF1dGhvcjogIkJyYW5kb24gTGVCZWF1Ig0KZGF0ZTogIkphbnVhcnkgMTAsIDIwMTgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIENvdXJzZSBTZXR1cA0KYGBge3Igc2V0dXBfY2h1bmtzLCBlY2hvID0gRkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTYsIGZpZy5jYXAgPSBOVUxMKSANCmBgYA0KDQpgYGB7ciBzZXR1cCwgbWVzc2FnZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9DQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KYGBgDQoNCmBgYHtyIGxpYnJhcnl9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQojIEV4cGxvcmUgRGF0YQ0KYGBge3IgZGF0YSwgZWNobyA9IEZBTFNFLCByZXN1bHRzID0gJ2FzaXMnfQ0KbWlkd2VzdA0KYGBgDQoNCiMgRmlyc3QgZ2dwbG90DQpgYGB7ciBwbG90MX0NCmdncGxvdChkYXRhID0gbWlkd2VzdCkgKw0KICBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlKSkNCmBgYA0KDQojIEVxdWl2YWxlbnQgQ29kZQ0KYGBge3IgcGxvdDFfcmVkdWNlZH0NCmdncGxvdChtaWR3ZXN0KSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSkpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIFRyeSBwbG90dGluZyBgcG9wZGVuc2l0eWAgYnkgYHN0YXRlYC4NCjIuIFRyeSBwbG90dGluZyBgY291bnR5YCBieSBgc3RhdGVgLiANCiAgICArIERvZXMgdGhpcyBwbG90IHdvcms/DQozLiBCb251czogVHJ5IGp1c3QgdXNpbmcgdGhlIGBnZ3Bsb3QoZGF0YSA9IG1pZHdlc3QpYCBmcm9tIGFib3ZlLiANCiAgICArIFdoYXQgZG8geW91IGdldD8gDQogICAgKyBEb2VzIHRoaXMgbWFrZSBzZW5zZT8NCg0KIyBBZGQgQWVzdGhldGljcw0KYGBge3IgYWVzdGhldGljfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkNCmBgYA0KDQojIEdsb2JhbCBBZXN0aGV0aWNzDQpgYGB7ciBnbG9iYWxfYWVzfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlKSwgY29sb3IgPSAncGluaycpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIEluc3RlYWQgb2YgdXNpbmcgY29sb3JzLCBtYWtlIHRoZSBzaGFwZSBvZiB0aGUgcG9pbnRzIGRpZmZlcmVudCBmb3IgZWFjaCBzdGF0ZS4NCjIuIEluc3RlYWQgb2YgY29sb3IsIHVzZSBgYWxwaGFgIGluc3RlYWQuIA0KICAgICsgV2hhdCBkb2VzIHRoaXMgZG8gdG8gdGhlIHBsb3Q/DQozLiBUcnkgdGhlIGZvbGxvd2luZyBjb21tYW5kOiBgY29sb3JzKClgLiANCiAgICArIFRyeSBhIGZldyBjb2xvcnMgdG8gZmluZCB5b3VyIGZhdm9yaXRlLg0KNC4gV2hhdCBoYXBwZW5zIGlmIHlvdSB1c2UgdGhlIGZvbGxvd2luZyBjb2RlOg0KYGBge3IgZ2xvYl9hZXMsIGZpZy5zaG93ID0gJ2hpZGUnfQ0KZ2dwbG90KG1pZHdlc3QpICsgDQogIGdlb21fcG9pbnQoYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSwgY29sb3IgPSAnZ3JlZW4nKSkNCmBgYA0KDQojIEFkZGl0aW9uYWwgR2VvbXMNCmBgYHtyIHNtb290aCwgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSkpDQpgYGANCg0KIyBBZGQgbW9yZSBBZXN0aGV0aWNzDQpgYGB7ciBzbW9vdGhfc3RhdGVzLCBtZXNzYWdlID0gRkFMU0V9DQpnZ3Bsb3QobWlkd2VzdCkgKw0KICBnZW9tX3Ntb290aChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBsaW5ldHlwZSA9IHN0YXRlKSwgDQogICAgICAgICAgICAgIHNlID0gRkFMU0UpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIEl0IGlzIHBvc3NpYmxlIHRvIGNvbWJpbmUgZ2VvbXMsIHdoaWNoIHdlIHdpbGwgZG8gbmV4dCwgYnV0IHRyeSBpdCBmaXJzdC4gVHJ5IHRvIHJlY3JlYXRlIHRoaXMgcGxvdC4NCmBgYHtyIGNvbWJpbmUsIGVjaG8gPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3Ntb290aChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSwgDQogICAgICAgICAgICAgIHNlID0gRkFMU0UpDQpgYGANCg0KIyBMYXllcmVkIGdncGxvdA0KYGBge3IgY29tYmluZV9nZW9tcywgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3Ntb290aChhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSwgDQogICAgICAgICAgICAgIHNlID0gRkFMU0UpDQpgYGANCg0KIyBSZW1vdmUgZHVwbGljYXRlIGFlc3RoZXRpY3MNCmBgYHtyIHR3b19nZW9tcywgbWVzc2FnZSA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QsIA0KICAgICAgIGFlcyh4ID0gcG9wZGVuc2l0eSwgeSA9IHBlcmNvbGxlZ2UsIGNvbG9yID0gc3RhdGUpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIENhbiB5b3UgcmVjcmVhdGUgdGhlIGZvbGxvd2luZyBmaWd1cmU/DQpgYGB7ciBkaWZmZXJfYWVzLCBtZXNzYWdlID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0NCmdncGxvdChtaWR3ZXN0LCBhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlKSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKQ0KYGBgDQoNCiMgQnJpZWYgcGxvdCBjdXN0b21pemF0aW9uDQpgYGB7ciBicmVha3NfeCwgZXZhbCA9IEZBTFNFfQ0KZ2dwbG90KG1pZHdlc3QsIA0KICAgICAgIGFlcyh4ID0gcG9wZGVuc2l0eSwgeSA9IHBlcmNvbGxlZ2UsIGNvbG9yID0gc3RhdGUpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoIlBvcHVsYXRpb24gRGVuc2l0eSIsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDgwMDAwLCAyMDAwMCkpICsgDQogIHNjYWxlX3lfY29udGludW91cygiUGVyY2VudCBDb2xsZWdlIEdyYWR1YXRlcyIpICsgDQogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKCJTdGF0ZSIpDQpgYGANCg0KIyBCcmllZiBwbG90IGN1c3RvbWl6YXRpb24gT3V0cHV0DQpgYGB7ciBicmVha3NfeDIsIGVjaG8gPSBGQUxTRX0NCmdncGxvdChtaWR3ZXN0LCANCiAgICAgICBhZXMoeCA9IHBvcGRlbnNpdHksIHkgPSBwZXJjb2xsZWdlLCBjb2xvciA9IHN0YXRlKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKCJQb3B1bGF0aW9uIERlbnNpdHkiLCANCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCA4MDAwMCwgMjAwMDApKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMoIlBlcmNlbnQgQ29sbGVnZSBHcmFkdWF0ZXMiKSArIA0KICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgiU3RhdGUiKQ0KYGBgDQoNCg0KIyBBZGRpdGlvbmFsIGdncGxvdDIgcmVzb3VyY2VzDQorIGdncGxvdDIgd2Vic2l0ZTogPGh0dHA6Ly9kb2NzLmdncGxvdDIub3JnL2N1cnJlbnQvaW5kZXguaHRtbD4NCisgZ2dwbG90MiBib29rOiA8aHR0cDovL3d3dy5zcHJpbmdlci5jb20vdXMvYm9vay85NzgwMzg3OTgxNDEzPg0KKyBSIGdyYXBoaWNzIGNvb2tib29rOiA8aHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvPg0KDQoNCiMgUiB3b3JrcyBhcyBhIGNhbGN1bGF0b3INCmBgYHtyIGNhbGN9DQoxICsgMiAtIDMNCjUgKiA3DQoyLzENCmBgYA0KDQojIFIgQ2FsY3VsYXRvciAyDQpgYGB7ciBjYWxjMn0NCnNxcnQoNCkNCjJeMg0KYGBgDQoNCg0KDQojIENhbiBzYXZlIG9iamVjdHMgdG8gdXNlIGxhdGVyDQpgYGB7ciBvYmplY3R9DQp4IDwtIDEgKyAzDQp4DQpgYGANCg0KYGBge3Igb2JqZWN0Mn0NCnggKiAzDQpgYGANCg0KIyBSIGlzIGNhc2Ugc2Vuc2l0aXZlDQpgYGB7ciBjYXNlLCBlcnJvciA9IFRSVUV9DQpjYXNlX3NlbnNpdGl2ZSA8LSAxMA0KQ2FzZV9zZW5zaXRpdmUNCmBgYA0KDQojIFIgRnVuY3Rpb25zDQpgYGB7ciBybm9ybX0NCnNldC5zZWVkKDEpDQpybm9ybShuID0gNSwgbWVhbiA9IDAsIHNkID0gMSkNCmBgYA0KYGBge3Igcm5vcm0yfQ0Kc2V0LnNlZWQoMSkNCnJub3JtKDUsIDAsIDEpDQpgYGANCmBgYHtyIHJub3JtM30NCnNldC5zZWVkKDEpDQpybm9ybShzZCA9IDEsIG4gPSA1LCBtZWFuID0gMCkNCmBgYA0KDQojIFdvcmtpbmcgdGhyb3VnaCBFcnJvcnMNCjEuIFVzZSBgP2Z1bmN0aW9uX25hbWVgIHRvIGV4cGxvcmUgdGhlIGRldGFpbHMgb2YgdGhlIGZ1bmN0aW9uLiBUaGUgZXhhbXBsZXMgYXQgdGhlIGJvdHRvbSBvZiBldmVyeSBSIGhlbHAgcGFnZSBjYW4gYmUgZXNwZWNpYWxseSBoZWxwZnVsLg0KICAgICsgPGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy8+IHByb3ZpZGVkIGJ5IERhdGFDYW1wIGlzIGEgZ3JlYXQgYWx0ZXJuYXRpdmUgYXMgd2VsbC4NCjIuIElmIHRoaXMgZG9lcyBub3QgaGVscCwgY29weSBhbmQgcGFzdGUgdGhlIGVycm9yIGFuZCBzZWFyY2ggb24gdGhlIGludGVybmV0Lg0KDQojIFVzaW5nIGBkcGx5cmAgZm9yIGRhdGEgbWFuaXB1bGF0aW9uDQpUaGUgYGRwbHlyYCBwYWNrYWdlIHVzZXMgdmVyYnMgZm9yIGNvbW1vbiBkYXRhIG1hbmlwdWxhdGlvbiB0YXNrcy4gVGhlc2UgaW5jbHVkZToNCg0KLSBgZmlsdGVyKClgDQotIGBjb3VudCgpYA0KLSBgYXJyYW5nZSgpYA0KLSBgc2VsZWN0KClgDQotIGBtdXRhdGUoKWANCi0gYHN1bW1hcmlzZSgpYA0KDQojIERhdGENCjxodHRwczovL2ZpdmV0aGlydHllaWdodC5jb20vZmVhdHVyZXMvYm90aC1yZXB1YmxpY2Fucy1hbmQtZGVtb2NyYXRzLWhhdmUtYW4tYWdlLXByb2JsZW0vPg0KDQpgYGB7ciBmaXZldGhpcnR5ZWlnaHQsIGV2YWwgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoJ2ZpdmV0aGlydHllaWdodCcpDQpsaWJyYXJ5KGZpdmV0aGlydHllaWdodCkNCmBgYA0KDQpgYGB7ciBjb25ncmVzcywgZWNobyA9IEZBTFNFfQ0KbGlicmFyeShmaXZldGhpcnR5ZWlnaHQpDQpjb25ncmVzc19hZ2VbMToxMDAsIF0NCmBgYA0KDQoNCiMgVXNpbmcgYGZpbHRlcmANCmBgYHtyIDgwdGh9DQpmaWx0ZXIoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcyA9PSA4MCkNCmBgYA0KDQojIFNhdmUgZmlsdGVyZWQgcmVzdWx0cyB0byBvYmplY3QNCmBgYHtyIDgwdGhfc2F2ZX0NCmNvbmdyZXNzXzgwIDwtIGZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzID09IDgwKQ0KYGBgDQoNCiMgT3RoZXIgb3BlcmF0b3JzIGZvciBudW1iZXJzDQotIGA+YA0KLSBgPGANCi0gYD49YA0KLSBgPD1gDQoNCiMgWW91ciBUdXJuDQoxLiBTZWxlY3QgYWxsIHJvd3Mgd2hlcmUgdGhlIGNvbmdyZXNzIG1lbWJlciB3YXMgb2xkZXIgdGhhbiA4MCBhdCB0aGUgc3RhcnQgb2YgdGhlIHRlcm0uDQoyLiBVc2UgdGhlIGBpcy5uYWAgZnVuY3Rpb24gdG8gaWRlbnRpZnkgY29uZ3Jlc3MgbWVtYmVycyB0aGF0IGhhdmUgbWlzc2luZyBtaWRkbGVuYW1lcy4NCg0KIyBGaWx0ZXIgY2hhcmFjdGVyIHZhcmlhYmxlcw0KYGBge3Igc2VuYXRlfQ0KZmlsdGVyKGNvbmdyZXNzX2FnZSwgY2hhbWJlciA9PSAnc2VuYXRlJykNCmBgYA0KDQojIENvbWJpbmUgT3BlcmF0aW9ucyAtIEFORA0KYGBge3IgODBzZW5hdGV9DQpmaWx0ZXIoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcyA9PSA4MCwgY2hhbWJlciA9PSAnc2VuYXRlJykNCmBgYA0KDQojIEVxdWl2YWxlbnQgQU5EIFN0YXRlbWVudA0KYGBge3IgODBzZW5hdGUyfQ0KZmlsdGVyKGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MgPT0gODAgJiBjaGFtYmVyID09ICdzZW5hdGUnKQ0KYGBgDQoNCiMgRmlsdGVyIC0gT1INCmBgYHtyIGZpbHRlcl9vcn0NCmZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzID09IDgwIHwgY29uZ3Jlc3MgPT0gODEpDQpgYGANCg0KIyBgJWluJWANCmBgYHtyIGZpbHRlcl8yfQ0KZmlsdGVyKGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MgJWluJSBjKDgwLCA4MSkpDQpgYGANCg0KIyBOb3QgT3BlcmF0b3INCmBgYHtyIG5vdF84MH0NCmZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzICE9IDgwKQ0KYGBgDQoNCiMgTm90IE9wZXJhdG9yIDINCmBgYHtyIDgwX25vdHNlbmF0ZX0NCmZpbHRlcihjb25ncmVzc19hZ2UsIGNvbmdyZXNzID09IDgwICYgIWNoYW1iZXIgPT0gJ3NlbmF0ZScpDQpgYGANCg0KIyBXb3JraW5nIHdpdGggbG9naWNhbHMNCmBgYHtyIGxvZ2ljYWxzfQ0KZmlsdGVyKGNvbmdyZXNzX2FnZSwgaW5jdW1iZW50KQ0KYGBgDQoNCg0KIyBZb3VyIFR1cm4NCjEuIFNlbGVjdCB0aGUgU2VuYXRvcnMgZnJvbSBJb3dhLg0KMi4gU2VsZWN0IHRoZSBTZW5hdG9ycyBmcm9tIElvd2EgdGhhdCBhcmUgbm90IGluYnVtYmVudHMuDQoNCiMgQWxsIGJvb2xlYW4gb3B0aW9ucw0KIVtdKGZpZ3VyZXMvYm9vbGVhbi5wbmcpDQoNCiMgVXNpbmcgYGNvdW50YA0KYGBge3IgY291bnR9DQpjb3VudChjb25ncmVzc19hZ2UsIHBhcnR5KQ0KY291bnQoY29uZ3Jlc3NfYWdlLCBpbmN1bWJlbnQpDQpgYGANCg0KIyBVc2luZyBgYXJyYW5nZWANCmBgYHtyIHNpbXBsZV9hcnJhbmdlfQ0KYXJyYW5nZShjb25ncmVzc19hZ2UsIHN0YXRlKQ0KYGBgDQoNCiMgQWRkIG1vcmUgdmFyaWFibGVzDQpgYGB7ciB0d29fYXJyYW5nZX0NCmFycmFuZ2UoY29uZ3Jlc3NfYWdlLCBzdGF0ZSwgcGFydHkpDQpgYGANCg0KIyBEZXNjZW5kaW5nIE9yZGVyDQpgYGB7ciBkZXNjZW5kfQ0KYXJyYW5nZShjb25ncmVzc19hZ2UsIGRlc2MoY29uZ3Jlc3MpKQ0KYGBgDQoNCiMgWW91ciBUdXJuDQoxLiBDb3VudCB0aGUgbnVtYmVyIG9mIGNvbmdyZXNzIG1lbWJlcnMgZnJvbSBlYWNoIHBhcnR5IHRoYXQgYXJlIG9sZGVyIHRoYW4gODAgYXQgdGVybSBzdGFydC4NCjIuIEFycmFuZ2UgdGhlIHJlc3VsdCBmcm9tIGFib3ZlIGJ5IHRoZSB2YXJpYWJsZSBuLg0KDQojIFVzaW5nIGBzZWxlY3RgDQpgYGB7ciBzZWxlY3R9DQpzZWxlY3QoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcywgY2hhbWJlciwgcGFydHksIGFnZSkNCmBgYA0KDQojIEhlbHBlciBmdW5jdGlvbnMNCi0gYHN0YXJ0c193aXRoKClgDQotIGBlbmRzX3dpdGgoKWANCi0gYGNvbnRhaW5zKClgDQotIGBtYXRjaGVzKClgDQotIGBudW1fcmFuZ2UoKWANCi0gYDpgDQotIGBldmVyeXRoaW5nKClgDQoNCiMgYHN0YXJ0c193aXRoYCBoZWxwZXINCmBgYHtyIHN0YXJ0c193aXRofQ0Kc2VsZWN0KGNvbmdyZXNzX2FnZSwgc3RhcnRzX3dpdGgoJ3MnKSkNCmBgYA0KDQoNCiMgQ29udGFpbnMgaGVscGVyDQpgYGB7ciBjb250YWluc30NCnNlbGVjdChjb25ncmVzc19hZ2UsIGNvbnRhaW5zKCduYW1lJykpDQpgYGANCg0KIyBDb2xvbg0KYGBge3IgY29sb259DQpzZWxlY3QoY29uZ3Jlc3NfYWdlLCBjb25ncmVzczpiaXJ0aGRheSkNCmBgYA0KDQojIERyb3AgdmFyaWFibGVzDQpgYGB7ciBkcm9wfQ0Kc2VsZWN0KGNvbmdyZXNzX2FnZSwgLWZpcnN0bmFtZSwgLXN0YXRlLCAtcGFydHksIC1pbmN1bWJlbnQsIC1jaGFtYmVyKQ0KYGBgDQoNCiMgUmVvcmRlciB3aXRoIGBldmVyeXRoaW5nYA0KYGBge3IgcmVvcmRlcn0NCnNlbGVjdChjb25ncmVzc19hZ2UsIGNvbmdyZXNzLCBjaGFtYmVyLCBpbmN1bWJlbnQsIGFnZSwgZXZlcnl0aGluZygpKQ0KYGBgDQoNCiMgYHJlbmFtZWAgZnVuY3Rpb24NCmBgYHtyIHJlbmFtZX0NCnJlbmFtZShjb25ncmVzc19hZ2UsIGZpcnN0X25hbWUgPSBmaXJzdG5hbWUsIGxhc3RfbmFtZSA9IGxhc3RuYW1lKQ0KYGBgDQoNCiMgWW91ciBUdXJuDQoxLiBVc2luZyB0aGUgYGRwbHlyYCBoZWxwZXIgZnVuY3Rpb25zLCBzZWxlY3QgYWxsIHRoZSB2YXJpYWJsZXMgdGhhdCBzdGFydCB3aXRoIHRoZSBsZXR0ZXIgJ2MnLg0KMi4gUmVuYW1lIHRoZSBmaXJzdCB0aHJlZSB2YXJpYWJsZXMgaW4gdGhlIGNvbmdyZXNzIGRhdGEgdG8gJ3gxJywgJ3gyJywgJ3gzJy4NCjMuIEFmdGVyIHJlbmFtaW5nIHRoZSBmaXJzdCB0aHJlZSB2YXJpYWJsZXMsIHVzZSB0aGlzIG5ldyBkYXRhIChlbnN1cmUgeW91IHNhdmVkIHRoZSBwcmV2aW91cyBzdGVwIHRvIGFuIG9iamVjdCkgdG8gc2VsZWN0IHRoZXNlIHRocmVlIHZhcmlhYmxlcyB3aXRoIHRoZSBgbnVtX3JhbmdlYCBmdW5jdGlvbi4NCg0KIyBVc2luZyBgbXV0YXRlYA0KYGBge3IgbXV0YXRlLCBlcnJvciA9IEZBTFNFfQ0KY29uZ3Jlc3NfcmVkIDwtIHNlbGVjdChjb25ncmVzc19hZ2UsIGNvbmdyZXNzLCBjaGFtYmVyLCBzdGF0ZSwgcGFydHkpDQoNCm11dGF0ZShjb25ncmVzc19yZWQsIA0KICAgICAgIGRlbW9jcmF0ID0gaWZlbHNlKHBhcnR5ID09ICdEJywgMSwgMCksDQogICAgICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KQ0KICAgICAgICkNCmBgYA0KDQojIFlvdXIgVHVybg0KMS4gVXNpbmcgdGhlIGBkaWFtb25kc2AgZGF0YSwgdXNlIGA/ZGlhbW9uZHNgIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZSBkYXRhLCB1c2UgdGhlIGBtdXRhdGVgIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgcHJpY2UgcGVyIGNhcmF0LiBIaW50LCB0aGlzIG9wZXJhdGlvbiB3b3VsZCBpbnZvbHZlIHN0YW5kYXJkaXppbmcgdGhlIHByaWNlIHZhcmlhYmxlIHNvIHRoYXQgYWxsIGFyZSBjb21wYXJhYmxlIGF0IDEgY2FyYXQuDQoyLiBVc2luZyBgbXV0YXRlYCwgY2FsY3VsYXRlIHRoZSByYW5rIG9mIHRoZSBvcmlnaW5hbCBwcmljZSB2YXJpYWJsZSBhbmQgdGhlIG5ldyBwcmljZSB2YXJpYWJsZSBjYWxjdWxhdGVkIGFib3ZlIHVzaW5nIHRoZSBgbWluX3JhbmtgIGZ1bmN0aW9uLiBBcmUgdGhlcmUgZGlmZmVyZW5jZXMgaW4gdGhlIHJhbmtpbmcgb2YgdGhlIHByaWNlcz8NCg0KIyBVc2luZyBgc3VtbWFyaXNlYA0KYGBge3Igc3VtbWFyaXNlfQ0KY29uZ3Jlc3NfMiA8LSBtdXRhdGUoY29uZ3Jlc3NfYWdlLCANCiAgICAgICBkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApDQogICAgICAgKQ0KDQpzdW1tYXJpc2UoY29uZ3Jlc3NfMiwgDQogICAgICAgICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KQ0KICAgICAgICAgICkNCmBgYA0KDQojIGBncm91cF9ieWANCmBgYHtyIGdyb3VwX2J5fQ0KY29uZ3Jlc3NfZ3JwIDwtIGdyb3VwX2J5KGNvbmdyZXNzXzIsIGNvbmdyZXNzKQ0KDQpzdW1tYXJpc2UoY29uZ3Jlc3NfZ3JwLCANCiAgICAgICAgICBudW1fZGVtb2NyYXQgPSBzdW0oZGVtb2NyYXQpLA0KICAgICAgICAgIHRvdGFsID0gbigpLA0KICAgICAgICAgIHByb3BfZGVtb2NyYXQgPSBudW1fZGVtb2NyYXQgLyB0b3RhbA0KKQ0KYGBgDQoNCiMgRXhwbG9yZSB0cmVuZA0KYGBge3IgdHJlbmQsIGV2YWwgPSBGQUxTRX0NCm51bV9kZW0gPC0gc3VtbWFyaXNlKGNvbmdyZXNzX2dycCwgDQogICAgICAgICAgICAgICAgICAgICBudW1fZGVtb2NyYXQgPSBzdW0oZGVtb2NyYXQpLA0KICAgICAgICAgICAgICAgICAgICAgdG90YWwgPSBuKCksDQogICAgICAgICAgICAgICAgICAgICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCikNCmdncGxvdChudW1fZGVtLCBhZXMoeCA9IGNvbmdyZXNzLCB5ID0gcHJvcF9kZW1vY3JhdCkpICsgDQogIGdlb21fbGluZSgpDQpgYGANCg0KIyBFeHBsb3JlIHRyZW5kIG91dHB1dA0KYGBge3IgdHJlbmQyLCBlY2hvID0gRkFMU0V9DQpudW1fZGVtIDwtIHN1bW1hcmlzZShjb25ncmVzc19ncnAsIA0KICAgICAgICAgICAgICAgICAgICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KSwNCiAgICAgICAgICAgICAgICAgICAgIHRvdGFsID0gbigpLA0KICAgICAgICAgICAgICAgICAgICAgcHJvcF9kZW1vY3JhdCA9IG51bV9kZW1vY3JhdCAvIHRvdGFsDQopDQpnZ3Bsb3QobnVtX2RlbSwgYWVzKHggPSBjb25ncmVzcywgeSA9IHByb3BfZGVtb2NyYXQpKSArIA0KICBnZW9tX2xpbmUoKQ0KYGBgDQoNCiMgWW91ciBUdXJuDQoxLiBTdXBwb3NlIHdlIHdhbnRlZCB0byBjYWxjdWxhdGUgdGhlIG51bWJlciBhbmQgcHJvcG9ydGlvbiBvZiByZXB1YmxpY2FucyBpbnN0ZWFkIG9mIGRlbW9jcmF0cywgYXNzdW1pbmcgdGhlc2UgYXJlIHRoZSBvbmx5IHR3byBwYXJ0aWVzLCBlZGl0IHRoZSBgc3VtbWFyaXNlYCBjb21tYW5kIGFib3ZlIHRvIGNhbGN1bGF0ZSB0aGVzZSB2YWx1ZXMuIA0KMi4gU3VwcG9zZSBpbnN0ZWFkIG9mIHVzaW5nIGBzdW0oZGVtb2NyYXQpYCBhYm92ZSwgd2UgdXNlZCBgbWVhbihkZW1vY3JhdClgLCB3aGF0IGRvZXMgdGhpcyB2YWx1ZSByZXR1cm4/IFdoeSBkb2VzIGl0IHJldHVybiB0aGlzIHZhbHVlPw0KDQoNCiMgYGdyb3VwX2J5YCB3aXRoIGBtdXRhdGVgDQpgYGB7ciBtdXRhdGVfZ3JvdXAsIGV2YWwgPSBGQUxTRX0NCmNvbmdyZXNzX3JlZCA8LSBzZWxlY3QoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcywgY2hhbWJlciwgc3RhdGUsIHBhcnR5KQ0KY29uZ3Jlc3NfZ3JwIDwtIGdyb3VwX2J5KGNvbmdyZXNzX3JlZCwgY29uZ3Jlc3MpDQoNCm11dGF0ZShjb25ncmVzc19ncnAsIA0KICAgICAgIGRlbW9jcmF0ID0gaWZlbHNlKHBhcnR5ID09ICdEJywgMSwgMCksDQogICAgICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KSwNCiAgICAgICB0b3RhbCA9IG4oKSwNCiAgICAgICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCikNCmBgYA0KDQojIGBncm91cF9ieWAgd2l0aCBgbXV0YXRlYCBvdXRwdXQNCmBgYHtyIG11dGF0ZV9ncm91cDIsIGVjaG8gPSBGQUxTRX0NCmNvbmdyZXNzX3JlZCA8LSBzZWxlY3QoY29uZ3Jlc3NfYWdlLCBjb25ncmVzcywgY2hhbWJlciwgc3RhdGUsIHBhcnR5KQ0KY29uZ3Jlc3NfZ3JwIDwtIGdyb3VwX2J5KGNvbmdyZXNzX3JlZCwgY29uZ3Jlc3MpDQoNCm11dGF0ZShjb25ncmVzc19ncnAsIA0KICAgICAgIGRlbW9jcmF0ID0gaWZlbHNlKHBhcnR5ID09ICdEJywgMSwgMCksDQogICAgICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KSwNCiAgICAgICB0b3RhbCA9IG4oKSwNCiAgICAgICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCikNCmBgYA0KDQojIENoYWluaW5nIG9wZXJhdGlvbnMNCmBgYHtyIGNoYWluX2RpZmZpY3VsdCwgZXZhbCA9IEZBTFNFfQ0Kc3VtbWFyaXNlKA0KICBncm91cF9ieSgNCiAgICBtdXRhdGUoDQogICAgICBmaWx0ZXIoDQogICAgICAgIGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MgPj0gMTAwDQogICAgICApLCANCiAgICAgIGRlbW9jcmF0ID0gaWZlbHNlKHBhcnR5ID09ICdEJywgMSwgMCkNCiAgICApLA0KICAgIGNvbmdyZXNzLCBjaGFtYmVyDQogICksDQogIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCksDQogIHRvdGFsID0gbigpLA0KICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCikNCmBgYA0KDQojIFRoZSBwaXBlIGAlPiVgIGlzIHRoZSBhbnN3ZXINCmBgYHtyIHBpcGUsIGV2YWwgPSBGQUxTRX0NCmNvbmdyZXNzX2FnZSAlPiUNCiAgZmlsdGVyKGNvbmdyZXNzID49IDEwMCkgJT4lDQogIG11dGF0ZShkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApKSAlPiUNCiAgZ3JvdXBfYnkoY29uZ3Jlc3MsIGNoYW1iZXIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KSwNCiAgICB0b3RhbCA9IG4oKSwNCiAgICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCiAgKQ0KYGBgDQoNCiMgVGhlIHR3byBhcmUgaWRlbnRpY2FsDQpgYGB7ciBpZGVudGljYWx9DQpwaXBlX2NvbmdyZXNzIDwtIGNvbmdyZXNzX2FnZSAlPiUNCiAgZmlsdGVyKGNvbmdyZXNzID49IDEwMCkgJT4lDQogIG11dGF0ZShkZW1vY3JhdCA9IGlmZWxzZShwYXJ0eSA9PSAnRCcsIDEsIDApKSAlPiUNCiAgZ3JvdXBfYnkoY29uZ3Jlc3MsIGNoYW1iZXIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbnVtX2RlbW9jcmF0ID0gc3VtKGRlbW9jcmF0KSwNCiAgICB0b3RhbCA9IG4oKSwNCiAgICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCiAgKQ0KDQpuZXN0ZWRfY29uZ3Jlc3MgPC0gc3VtbWFyaXNlKA0KICBncm91cF9ieSgNCiAgICBtdXRhdGUoDQogICAgICBmaWx0ZXIoDQogICAgICAgIGNvbmdyZXNzX2FnZSwgY29uZ3Jlc3MgPj0gMTAwDQogICAgICApLCANCiAgICAgIGRlbW9jcmF0ID0gaWZlbHNlKHBhcnR5ID09ICdEJywgMSwgMCkNCiAgICApLA0KICAgIGNvbmdyZXNzLCBjaGFtYmVyDQogICksDQogIG51bV9kZW1vY3JhdCA9IHN1bShkZW1vY3JhdCksDQogIHRvdGFsID0gbigpLA0KICBwcm9wX2RlbW9jcmF0ID0gbnVtX2RlbW9jcmF0IC8gdG90YWwNCikNCg0KaWRlbnRpY2FsKHBpcGVfY29uZ3Jlc3MsIG5lc3RlZF9jb25ncmVzcykNCmBgYA0KDQoNCiMgWW91ciBUdXJuDQoxLiBMb29rIGF0IHRoZSBmb2xsb3dpbmcgbmVzdGVkIGNvZGUgYW5kIGRldGVybWluZSB3aGF0IGlzIGJlaW5nIGRvbmUuIFRoZW4gdHJhbnNsYXRlIHRoaXMgY29kZSB0byB1c2UgdGhlIHBpcGUgb3BlcmF0b3IuDQpgYGB7ciBjb2RlX3BpcGVfZXhhbXAsIGV2YWwgPSBGQUxTRX0NCnN1bW1hcmlzZSgNCiAgZ3JvdXBfYnkoDQogICAgbXV0YXRlKA0KICAgICAgZmlsdGVyKA0KICAgICAgICBkaWFtb25kcywgDQogICAgICAgIGNvbG9yICVpbiUgYygnRCcsICdFJywgJ0YnKSAmIGN1dCAlaW4lIGMoJ0ZhaXInLCAnR29vZCcsICdWZXJ5IEdvb2QnKQ0KICAgICAgKSwNCiAgICAgIGZfY29sb3IgPSBpZmVsc2UoY29sb3IgPT0gJ0YnLCAxLCAwKSwNCiAgICAgIHZnX2N1dCA9IGlmZWxzZShjdXQgPT0gJ1ZlcnkgR29vZCcsIDEsIDApDQogICAgKSwNCiAgICBjbGFyaXR5DQogICksDQogIGF2ZyA9IG1lYW4oY2FyYXQpLA0KICBzZCA9IHNkKGNhcmF0KSwNCiAgYXZnX3AgPSBtZWFuKHByaWNlKSwNCiAgbnVtID0gbigpLA0KICBzdW1tYXJ5X2ZfY29sb3IgPSBtZWFuKGZfY29sb3IpLA0KICBzdW1tYXJ5X3ZnX2N1dCA9IG1lYW4odmdfY3V0KQ0KKQ0KYGBgDQoNCiMgT3RoZXIgVXNlZnVsIGRwbHlyIGZ1bmN0aW9ucw0KVGhlcmUgYXJlIGEgc2V0IG9mIGZ1bmN0aW9ucyB0aGF0IGNhbiBncmVhdGx5IHNpbXBsaWZ5IGRhdGEgb3BlcmF0aW9ucy4gVGhlc2UgZnVuY3Rpb25zIGVuZCB3aXRoOg0KDQorIGAqX2lmYCANCisgYCpfZWFjaGAgDQorIGAqX2FsbGANCisgYCpfYXRgDQoNCiMgU2ltcGxlIGV4YW1wbGUNCmBgYHtyIHJlbmFtZV9pZn0NCnJlbmFtZV9pZihjb25ncmVzc19hZ2UsIGlzLmNoYXJhY3RlciwgdG91cHBlcikNCmBgYA0KDQojIE1vcmUgcmVhbGlzdGljIGV4YW1wbGUNCmBgYHtyIHN1bW1hcmlzZV9pZn0NCnN0YXJ3YXJzICU+JQ0KICBncm91cF9ieShzcGVjaWVzLCBnZW5kZXIpICU+JQ0KICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgbWVhbiwgbmEucm0gPSBUUlVFKQ0KYGBgDQoNCiMgQ2FuIGNhbGwgbW9yZSB0aGFuIG9uZSBmdW5jdGlvbg0KYGBge3Igc3VtbWFyaXNlX2lmMn0NCnN0YXJ3YXJzICU+JQ0KICBncm91cF9ieShzcGVjaWVzLCBnZW5kZXIpICU+JQ0KICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgZnVucyhtZWFuLCBtZWRpYW4pLCBuYS5ybSA9IFRSVUUpICU+JQ0KICBhZGRfdGFsbHkoKQ0KYGBgDQoNCg0KDQojIFJlYWQgaW4geW91ciBvd24gZGF0YQ0KLSBXZSB3aWxsIHVzZSBkYXRhIHBvc3RlZCB0byBHaXRIdWI6IDxodHRwczovL2dpdGh1Yi5jb20vbGViZWJyMDEvaW93YV9kYXRhX3NjaWVuY2UvdHJlZS9tYXN0ZXIvZGF0YT4NCg0KIyBEYXRhIEltcG9ydA0KYGBge3IgdWZvX3JlYWR9DQp1Zm8gPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sZWJlYnIwMS9pb3dhX2RhdGFfc2NpZW5jZS9tYXN0ZXIvZGF0YS91Zm8uY3N2IikNCmBgYA0KDQojIFNob3cgRGF0YQ0KYGBge3IgdWZvX2RhdGF9DQp1Zm8NCmBgYA0KDQoNCiMgTWFudWFsIGNvbHVtbiBuYW1lcw0KYGBge3IgdWZvX21hbnVhbF9uYW1lc30NCnVmb19tYW4gPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9sZWJlYnIwMS9pb3dhX2RhdGFfc2NpZW5jZS9tYXN0ZXIvZGF0YS91Zm8uY3N2IiwgDQogICAgICAgICBza2lwID0gMSwgDQogICAgICAgICBjb2xfbmFtZXMgPSBjKCdEYXRlL1RpbWUnLCAnQ2l0eScsICdTdGF0ZScsIA0KICAgICAgICAgICAgICAgICAgICAgICAnU2hhcGUnLCAnRHVyYXRpb24nLCAnU3VtbWFyeScsDQogICAgICAgICAgICAgICAgICAgICAgICdQb3N0ZWQnKSkNCmBgYA0KDQojIE1hbnVhbCBjb2x1bW4gbmFtZXMgb3V0cHV0DQpgYGB7ciB1Zm9fb3V0cHV0fQ0KdWZvX21hbg0KYGBgDQoNCg0KIyBNYW51YWwgY29sdW1uIHR5cGVzDQpgYGB7ciBkYXRlX3RpbWUyfQ0KdWZvX2RhdGUgPC0gcmVhZF9jc3YoIkRhdGEvdWZvLmNzdiIsIA0KICAgICAgICAgY29sX3R5cGVzID0gbGlzdCgNCiAgICAgICAgICAgJ0RhdGUgLyBUaW1lJyA9IGNvbF9kYXRldGltZShmb3JtYXQgPSAiJW0vJWQvJXkgJUg6JU0iKSwNCiAgICAgICAgICAgQ2l0eSA9IGNvbF9jaGFyYWN0ZXIoKSwNCiAgICAgICAgICAgU3RhdGUgPSBjb2xfY2hhcmFjdGVyKCksDQogICAgICAgICAgIFNoYXBlID0gY29sX2NoYXJhY3RlcigpLA0KICAgICAgICAgICBEdXJhdGlvbiA9IGNvbF9jaGFyYWN0ZXIoKSwNCiAgICAgICAgICAgU3VtbWFyeSA9IGNvbF9jaGFyYWN0ZXIoKSwNCiAgICAgICAgICAgUG9zdGVkID0gY29sX2NoYXJhY3RlcigpDQogICAgICAgICApKQ0KYGBgDQoNCiMgTWFudWFsIGNvbHVtbiB0eXBlcyBvdXRwdXQNCmBgYHtyIHVmb190eXBlc30NCnVmb19kYXRlDQpgYGANCg0KDQojIFN0aWxsIHByb2JsZW1zDQpgYGB7ciBwcm9ibGVtc30NCnByb2JsZW1zKHVmb19kYXRlKQ0KYGBgDQogICAgDQoNCiMgT3RoZXIgdGV4dCBmb3JtYXRzDQotIHRzdiAtIHRhYiBzZXBhcmF0ZWQgZmlsZXMgLSBgcmVhZF90c3ZgDQotIGZpeGVkIHdpZHRoIGZpbGVzIC0gYHJlYWRfZndmYA0KLSB3aGl0ZSBzcGFjZSBnZW5lcmFsbHkgLSBgcmVhZF90YWJsZWANCi0gZGVsaW1pdGVyIGdlbmVyYWxseSAtIGByZWFkX2RlbGltYA0KDQojIFlvdXIgVHVybg0KMS4gVGhlcmUgaXMgYSB0c3YgZmlsZSBwb3N0ZWQgb24gaWNvbiBjYWxsZWQgImxvdHJfY2xlYW4udHN2Ii4gRG93bmxvYWQgdGhpcyBhbmQgcmVhZCB0aGlzIGRhdGEgZmlsZSBpbnRvIFIuIA0KMi4gSW5zdGVhZCBvZiBzcGVjaWZ5aW5nIHRoZSBwYXRoLCB1c2UgdGhlIGZ1bmN0aW9uIGBmaWxlLmNob29zZSgpYC4gRm9yIGV4YW1wbGUsIGByZWFkX3RzdihmaWxlLmNob29zZSgpKWAuIA0KICAgICsgV2hhdCBkb2VzIHRoaXMgZnVuY3Rpb24gZG8/IA0KICAgICsgV291bGQgeW91IHJlY29tbWVuZCB0aGlzIHRvIGJlIHVzZWQgaW4gYSByZXByb2R1Y2libGUgZG9jdW1lbnQ/DQogICAgDQojIEV4Y2VsIEZpbGVzDQpgYGB7ciByZWFkeGwsIGV2YWwgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoJ3JlYWR4bCcpDQpgYGANCg0KIyBgcmVhZF9leGNlbGANCmBgYHtyIHJlYWRfZXhjZWx9DQpsaWJyYXJ5KHJlYWR4bCkNCnJlYWRfZXhjZWwoJ2RhdGEvdGl0YW5pYy54bHN4JykNCmBgYA0KDQojIFdyaXRlIE91dHB1dCBGaWxlcw0KYGBge3Igd3JpdGUsIGV2YWwgPSBGQUxTRX0NCnVmb19jb3VudCA8LSB1Zm8gJT4lDQogIGdyb3VwX2J5KFN0YXRlKSAlPiUNCiAgbXV0YXRlKG51bV9zdGF0ZSA9IG4oKSkNCg0Kd3JpdGVfY3N2KHVmb19jb3VudCwgcGF0aCA9ICdwYXRoL3RvL3NhdmUvZmlsZS5jc3YnKQ0KYGBgDQoNCiMgQWRkaXRpb25hbCBSZXNvdXJjZXMNCisgUiBmb3IgRGF0YSBTY2llbmNlOiA8aHR0cDovL3I0ZHMuaGFkLmNvLm56Lz4NCg==